code
服务端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using namespace std;
int main(){
int listenfd,connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERV_PORT);
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
int i=0;
for(;;){
clilen=sizeof(cliaddr);
connfd=accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpid=fork())==0){
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
i++;
cout<<i<<" "<<endl;
}
return 0;
}
void str_echo(int sockfd){
ssize_t n;
char buf[MAXLINE];
again:
while((n=read(sockfd,buf,MAXLINE))>0){
write(sockfd,buf,n);
}
if(n<0 && errno==EINTR){
goto again;
}else if(n<0){
cout<<"str_echo: read error"<<endl;
}
}客户端程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using namespace std;
int main(int argc,char **argv){
int sockfd;
struct sockaddr_in servaddr;
if(argc!=2){
cout<<"usage:echoclie <ip addr>"<<endl;
return 0;
}
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
cout<<"socket create error"<<endl;
return 0;
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERV_PORT);
inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr));
if(conn<0){
cout<<"connect create error"<<endl;
return 0;
}
str_cli(stdin,sockfd);
return 0;
}
void str_cli(FILE *fp,int sockfd){
char sendline[MAXLINE],recvline[MAXLINE];
while(fgets(sendline,MAXLINE,fp) != NULL){
write(sockfd,sendline,strlen(sendline));
//if(readline(sockfd,recvline,MAXLINE)==0){
if(read(sockfd,recvline,MAXLINE)<0){
cout<<"str_cli:server terminated prematurely"<<endl;
}
fputs(recvline,stdout);
}
}
TCP连接过程
服务端用到的几个网络编程api
建立socket套接字
- 函数原型
1
2
3
4
5
6
7
8
9
10
11
12int socket(int domain, int type, int protocol);
domain 具体通信的域
AF_UNIX Local communication
AF_LOCAL Synonym for AF_UNIX
AF_INET(常用) IPv4 Internet protocols
type 通信类型
SOCK_STREAM(常用) Provides sequenced, reliable, two-way, connection-
based byte streams. An out-of-band data transmission
mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable
messages of a fixed maximum length).
protocol 用来设置用tcp还是udp,一般为0
sockaddr_in结构
1 | include <netinet/in.h> |
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:
程序员不应操作sockaddr,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。
一般的用法为:
程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数
bzero置零函数
memset()函数也可完成这个功能,但是在填写参数时容易出错并且编译器不会提示,所以改用bzero1
2
3
4include <strings.h>
void bzero(void *s, size_t n);
%%%%
bzero(&servaddr,sizeof(servaddr));
字节序转换hton函数
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);1
2
3
4
5
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
用来将主机字节序的无符号整型转换为网络字节序
bind将ip和端口绑定到socket
1 | bind(listenfd,(SA*)&servaddr,sizeof(servaddr)); |
监听套接字listen
listen(listenfd,LISTENQ);1
2
3
4
5include <sys/socket.h>
int listen(int socket,int backlog)
socket 监听的套接字描述符
backlog
系统维护两个队列,分别是已完成三路握手的队列和未完成的,两个队列的长度不会超过backlog
接受连接accept
1 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
客户端用到的api
发起连接请求connect
1 | int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr)); |
参考
- 《UNIX网络编程(卷一)》
- sockaddr和sockaddr_in的区别
欢迎与我分享你的看法。
转载请注明出处:http://taowusheng.cn/